package cn.sunxyz.aop.proxy;

import cn.sunxyz.aop.aspect.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created by yangrd on 2017/5/25.
 */
public class ProxyFactory {

    private static final Logger logger = LoggerFactory.getLogger(ProxyFactory.class);

    /**
     * key :=> 排序字段, value :=> Aspect 优先推荐AdviceAspect接口对应的抽象类AbstractAdviceAspect
     */
    public static <T> T getProxy(Class<T> cls, Map<Integer, Aspect> sortAspects) {
        return getProxy(cls, sortAspects.entrySet().stream().map(sortEntry -> sortEntry.getValue()).collect(Collectors.toList()));
    }

    /**
     * aspects 最好已经排序 此处不负责排序
     */
    public static <T> T getProxy(Class<T> cls, Collection<Aspect> aspects) {
        if (aspects != null) {
            List<AdviceAspect> adviceAspects = new ArrayList<>();
            List<InvocationHandler> invocationHandlers = new ArrayList<>();
            for (Aspect aspect : aspects) {
                if (aspect instanceof AdviceAspect) {
                    adviceAspects.add((AdviceAspect) aspect);
                } else if (aspect instanceof InvocationHandler) {
                    invocationHandlers.add((InvocationHandler) aspect);
                }
            }
            InvocationHandler defaultInvocationHandler = buildInvocationHandler(adviceAspects, invocationHandlers);
            HandlerProxy handlerProxy = ProxyManger.getProxy(ProxyManger.Crate.HANDLER_PROXY);
            handlerProxy.setInvocationHandler(defaultInvocationHandler);//设置默认的InvocationHandler
            logger.debug("[build Proxy] :=> class : {}", cls.getName());
            return handlerProxy.getProxy(cls);
        }
        return null;
    }

    static InvocationHandler buildInvocationHandler(List<AdviceAspect> adviceAspects, List<InvocationHandler> invocationHandlers) {
        //此处多了一层 默认代理 -> 链表 (可以通过空判断减少该层)
        InvocationHandler defaultInvocationHandler = new AbstractInvocationHandler() {
        };
        InvocationHandler lastInvocationHandler = defaultInvocationHandler;
        for (InvocationHandler handler : invocationHandlers) {
            lastInvocationHandler.setHandler(handler);
            lastInvocationHandler = handler;
        }
        //将两个代理模式进行串联 handlerProxy -> adviceProxy
        if (!adviceAspects.isEmpty()) {
            AdviceProxy adviceProxy = ProxyManger.getProxy(ProxyManger.Crate.ADVICE_PROXY);
            adviceProxy.addAdviceAspectAll(adviceAspects);
            //提供代理的适配器
            InvocationHandler invocationHandlerAdapter = new InvocationHandlerAdapter(adviceProxy);
            lastInvocationHandler.setHandler(invocationHandlerAdapter);
        }
        return defaultInvocationHandler;
    }

    static class InvocationHandlerAdapter extends AbstractInvocationHandler {
        private AdviceProxy adviceProxy;

        public InvocationHandlerAdapter(AdviceProxy adviceProxy) {
            this.adviceProxy = adviceProxy;
        }

        @Override
        public Object handlerInvocation(Invocation invocation) throws Throwable {
            return adviceProxy.intercept(invocation.obj, invocation.method, invocation.args, invocation.proxy);
        }
    }


}
